<html>
<head><meta charset="utf-8"><title>Move of special unions in const-eval · t-lang/wg-unsafe-code-guidelines · Zulip Chat Archive</title></head>
<h2>Stream: <a href="https://rust-lang.github.io/zulip_archive/stream/136281-t-lang/wg-unsafe-code-guidelines/index.html">t-lang/wg-unsafe-code-guidelines</a></h2>
<h3>Topic: <a href="https://rust-lang.github.io/zulip_archive/stream/136281-t-lang/wg-unsafe-code-guidelines/topic/Move.20of.20special.20unions.20in.20const-eval.html">Move of special unions in const-eval</a></h3>

<hr>

<base href="https://rust-lang.zulipchat.com">

<head><link href="https://rust-lang.github.io/zulip_archive/style.css" rel="stylesheet"></head>

<a name="189129866"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/136281-t-lang/wg-unsafe-code-guidelines/topic/Move%20of%20special%20unions%20in%20const-eval/near/189129866" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> HeroicKatora <a href="https://rust-lang.github.io/zulip_archive/stream/136281-t-lang/wg-unsafe-code-guidelines/topic/Move.20of.20special.20unions.20in.20const-eval.html#189129866">(Feb 26 2020 at 16:22)</a>:</h4>
<p>This is a question for clarification which I didn't quite want to make into an issue on the main repo, miri or unsafe-code-guidelines, and it is partially addressed by a thread in the repository already.</p>
<p>The constant evaluation of a move of some unions seems to differ from the semantics of non-constant evaluation. The union instance is not regarded as a bag-of-bits as aluded to in the reference and also <a href="https://github.com/rust-lang/unsafe-code-guidelines/issues/73#issue-397775312" target="_blank" title="https://github.com/rust-lang/unsafe-code-guidelines/issues/73#issue-397775312">this <code>unsafe-code-guidelines</code> issue</a>. This occurs only if the unions has a single scalar <em>scalar</em> field and otherwise ZSTs, which is pretty specific but occurs for <code>MaybeUninit</code> if the type parameter is a scalar. If the field is of other types such as arrays (<code>[scalar; N]</code>) <em>or</em> if the other field is not a ZST then the semantics seem to agree.</p>
<p>I tried this code:</p>
<div class="codehilite"><pre><span></span><span class="cp">#![feature(const_if_match)]</span><span class="w"> </span><span class="c1">// Only related to make_1u8_bag, all of them set by Miri</span>
<span class="cp">#![feature(const_fn)]</span><span class="w"></span>
<span class="cp">#![feature(const_mut_refs)]</span><span class="w"></span>
<span class="cp">#![feature(const_panic)]</span><span class="w"></span>
<span class="cp">#![feature(const_raw_ptr_deref)]</span><span class="w"></span>

<span class="c1">// Or, equivalently: `MaybeUninit`.</span>
<span class="k">pub</span><span class="w"> </span><span class="k">union</span> <span class="nc">BagOfBits</span><span class="o">&lt;</span><span class="n">T</span>: <span class="nb">Copy</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="n">uninit</span>: <span class="p">(),</span><span class="w"></span>
<span class="w">    </span><span class="n">_storage</span>: <span class="nc">T</span><span class="p">,</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="k">pub</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="k">fn</span> <span class="nf">make_1u8_bag</span><span class="o">&lt;</span><span class="n">T</span>: <span class="nb">Copy</span><span class="o">&gt;</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">BagOfBits</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="n">assert</span><span class="o">!</span><span class="p">(</span><span class="n">core</span>::<span class="n">mem</span>::<span class="n">size_of</span>::<span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">()</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w"></span>
<span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">bag</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BagOfBits</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">uninit</span>: <span class="p">()</span><span class="w"> </span><span class="p">};</span><span class="w"></span>
<span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">bag</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="kt">u8</span><span class="p">).</span><span class="n">write</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span><span class="w"> </span><span class="p">};</span><span class="w"></span>
<span class="w">    </span><span class="n">bag</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">check_bag</span><span class="o">&lt;</span><span class="n">T</span>: <span class="nb">Copy</span><span class="o">&gt;</span><span class="p">(</span><span class="n">bag</span>: <span class="kp">&amp;</span><span class="nc">BagOfBits</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">val</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">(</span><span class="n">bag</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u8</span><span class="p">).</span><span class="n">read</span><span class="p">()</span><span class="w"> </span><span class="p">};</span><span class="w"></span>
<span class="w">    </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="n">val</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="n">check_bag</span><span class="p">(</span><span class="o">&amp;</span><span class="n">make_1u8_bag</span>::<span class="o">&lt;</span><span class="p">[</span><span class="kt">usize</span><span class="p">;</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="o">&gt;</span><span class="p">());</span><span class="w"> </span><span class="c1">// Fine</span>
<span class="w">    </span><span class="n">check_bag</span><span class="p">(</span><span class="o">&amp;</span><span class="n">make_1u8_bag</span>::<span class="o">&lt;</span><span class="kt">usize</span><span class="o">&gt;</span><span class="p">());</span><span class="w"> </span><span class="c1">// Fine</span>

<span class="w">    </span><span class="k">const</span><span class="w"> </span><span class="n">CONST_ARRAY_BAG</span>: <span class="nc">BagOfBits</span><span class="o">&lt;</span><span class="p">[</span><span class="kt">usize</span><span class="p">;</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">make_1u8_bag</span><span class="p">();</span><span class="w"></span>
<span class="w">    </span><span class="n">check_bag</span><span class="p">(</span><span class="o">&amp;</span><span class="n">CONST_ARRAY_BAG</span><span class="p">);</span><span class="w"> </span><span class="c1">// Fine.</span>
<span class="w">    </span><span class="k">const</span><span class="w"> </span><span class="n">CONST_USIZE_BAG</span>: <span class="nc">BagOfBits</span><span class="o">&lt;</span><span class="kt">usize</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">make_1u8_bag</span><span class="p">();</span><span class="w"></span>
<span class="w">    </span><span class="n">check_bag</span><span class="p">(</span><span class="o">&amp;</span><span class="n">CONST_USIZE_BAG</span><span class="p">);</span><span class="w"> </span><span class="c1">// Panics</span>
<span class="p">}</span><span class="w"></span>
</pre></div>


<p><a href="https://play.rust-lang.org/?version=nightly&amp;mode=release&amp;edition=2018&amp;gist=deecbf5c59706a37f7db16ea5f3ed30b" target="_blank" title="https://play.rust-lang.org/?version=nightly&amp;mode=release&amp;edition=2018&amp;gist=deecbf5c59706a37f7db16ea5f3ed30b">Playground</a></p>
<p>I expected to see this happen: All assertions succeed.</p>
<p>Instead, this happened: The check where a union of type <code>BagOfBits&lt;usize&gt;</code> is <em>moved during constant evaluation</em>, panics. When putting everything through Miri it panics with the message of trying to read an uninitialized byte. The check where the union of type <code>BagOfBits&lt;[usize; 1]&gt;</code> is used instead runs fine in both cases.</p>
<div class="codehilite"><pre><span></span>error: Miri evaluation error: attempted to read undefined bytes
  --&gt; src/main.rs:21:5
   |
21 |     assert_eq!(val, 1);
   |     ^^^^^^^^^^^^^^^^^^^ attempted to read undefined bytes
</pre></div>


<p>As far as I am aware, when constant evaluation reads <em>scalars</em> then it propagates uninitialized bytes. If that were to happen then it would explain the observed behaviour but it seems weird why the union copy would use a scalar read.</p>
<p>Now, the question is: Are there any <em>stable</em> guarantees that this behaviour violates? If not, are there any that are likely to be given in the future? And if not, is the behaviour for a field of type <code>[usize; 1]</code> or a second non-ZST field stably sound?</p>



<a name="189131685"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/136281-t-lang/wg-unsafe-code-guidelines/topic/Move%20of%20special%20unions%20in%20const-eval/near/189131685" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> RalfJ <a href="https://rust-lang.github.io/zulip_archive/stream/136281-t-lang/wg-unsafe-code-guidelines/topic/Move.20of.20special.20unions.20in.20const-eval.html#189131685">(Feb 26 2020 at 16:38)</a>:</h4>
<p><span class="user-mention" data-user-id="229913">@HeroicKatora</span> interesting find! I think this is more in const-eval territory than UCG. And I think it is a bug.<br>
What happens, most likely, is that const eval realizes that <code>BagOfBits&lt;usize&gt;</code> is a <code>Scalar</code>, and <a href="https://github.com/rust-lang/rust/blob/892cb143e5984f220e6b26b48d972bd1f4644298/src/librustc_mir/const_eval/eval_queries.rs#L109" target="_blank" title="https://github.com/rust-lang/rust/blob/892cb143e5984f220e6b26b48d972bd1f4644298/src/librustc_mir/const_eval/eval_queries.rs#L109">scalars get some special treatment</a> that "amplifies" a partially uninitialized result into being fully uninitialized.<br>
But before we start having a discussion here that will get lost -- could you please open an issue, and Cc me? This is worth tracking.</p>



<a name="189132184"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/136281-t-lang/wg-unsafe-code-guidelines/topic/Move%20of%20special%20unions%20in%20const-eval/near/189132184" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> HeroicKatora <a href="https://rust-lang.github.io/zulip_archive/stream/136281-t-lang/wg-unsafe-code-guidelines/topic/Move.20of.20special.20unions.20in.20const-eval.html#189132184">(Feb 26 2020 at 16:44)</a>:</h4>
<p>If you too think this is a bug, then I'll open a tracking issue <span aria-label="smiley" class="emoji emoji-1f603" role="img" title="smiley">:smiley:</span></p>



<hr><p>Last updated: Aug 07 2021 at 22:04 UTC</p>
</html>